/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "napi/native_api.h"
#include "napi/native_node_api.h"
#include "utils/log.h"

#include <stdlib.h>     // standard library 标准库函数头文件
#include <stdio.h>      // standard input output 标准输入输出函数
#include <stdint.h>     // 定义了扩展的整数类型和宏

#include <unistd.h>     // POSIX 系统 API 访问功能的头文件
#include <fcntl.h>      // unix标准中通用的头文件 define O_WRONLY and O_RDONLY 

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>

#define GPIO_DIR_IN (char*)"in"
#define GPIO_DIR_OUT (char*)"out"
#define GPIO_VAL_LOW 0
#define GPIO_VAL_HIGH 1



//led
static napi_value SwitchLed(napi_env env, napi_callback_info info)
{
    HILOG_INFO("test0002 SwitchLed - 0");
    HILOG_DEBUG("test0002 debug SwitchLed - 0");
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
    napi_valuetype valuetype1;
    NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));

    NAPI_ASSERT(env, valuetype0 == napi_number && valuetype1 == napi_number, "Wrong argument type. Numbers expected.");

    uint32_t gpio;
    NAPI_CALL(env, napi_get_value_uint32(env, args[0], &gpio));

    uint32_t val;
    NAPI_CALL(env, napi_get_value_uint32(env, args[1], &val));


	char direction[100] = {0};
    sprintf(direction,"echo out > /sys/class/gpio/gpio%d/direction", gpio);
    system(direction);

    char value[100] = {0};
    sprintf(value,"echo %d > /sys/class/gpio/gpio%d/value", val, gpio);
    system(value);

    napi_value sum;
    NAPI_CALL(env, napi_create_double(env, 1.0f, &sum));
    return sum;
}


static napi_value JSCallback(napi_env env, napi_callback_info info)
{

    size_t argc = 1; //参数个数定义
    napi_value argv[argc];
    napi_value thisVar = nullptr;
    void *data = nullptr;
	napi_ref CallbackRef;
	napi_value jsObj, prop1,prop2, callback = nullptr,undefine = nullptr;

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
    NAPI_ASSERT(env, argc >= 1, "JSCallback Wrong number of arguments"); //参数个数校验

    napi_valuetype callbackType = napi_undefined;
    napi_typeof(env, argv[0], &callbackType);
    NAPI_ASSERT(env, callbackType == napi_function, "parameter 1 type mismatch"); //参数类型校验，传进来的须是函数类型

	napi_create_reference(env, argv[0], 1, &CallbackRef);  //创建引用
	napi_get_reference_value(env, CallbackRef, &callback); //根据引用获取回调函数callback

	int cPara = 1;
    int cPara2 = 2;

	napi_create_object(env, &jsObj); //创建JS回调函数对应的参数对象
	napi_create_int32(env, cPara, &prop1);
    napi_create_int32(env, cPara2, &prop2);
	napi_set_named_property(env, jsObj, "prop", prop1); //设置JS参数对象属性值
    napi_set_named_property(env, jsObj, "prop2", prop2);

	napi_call_function(env, nullptr, callback, 1, &jsObj, &undefine); //使用生成的JS参数，调用对应的JS回调函数

    napi_call_function(env, nullptr, callback, 1, &jsObj, &undefine); //使用生成的JS参数，调用对应的JS回调函数

    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}


static char buf[1024];
static size_t buflen;
static int wflag;
static int st;
static int client_st;

static napi_ref CallbackReff;
static napi_env envs;


//sockerServer 线程
void* socketserverthrd(void *ptr)
{
    napi_value jsObj, prop1,prop2,prop3, callback = nullptr,undefine = nullptr;
    napi_get_reference_value(envs, CallbackReff, &callback);

    int port = 18000;
    st = socket(AF_INET, SOCK_STREAM, 0);
    int opt = SO_REUSEADDR;
    setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)
    {
        HILOG_INFO("test0002 bind failed %s\n", strerror(errno));
        return NULL;
    }

    if (listen(st, 20) == -1)
    {
        HILOG_INFO("test0002 listen failed %s\n", strerror(errno));
        return NULL;
    }
    HILOG_INFO("test0002 listen success\n");

    struct sockaddr_in client_addr;
    memset(&client_addr, 0, sizeof(client_addr));
    socklen_t len = sizeof(client_addr);
    HILOG_INFO("test0002 waiting for client.......\n");
    wflag = 1;
    char str[1024];
    while(wflag)
    {
        client_st = accept(st, (struct sockaddr*) &client_addr, &len);
        if (client_st == -1)
        {
            HILOG_INFO("test0002  accept failed %s\n", strerror(errno));
            return NULL;
        }
            HILOG_INFO("test0002 accept by %s\n", inet_ntoa(client_addr.sin_addr));
            while (wflag)
            {
                memset(str, 0, sizeof(str));
                int numbytes = recv(client_st, str, sizeof(str), 0);
                if (numbytes <= 0)
                        break;
                 //if(wflag == 0) break;
                strcpy(buf,str);
                if((int)str[0] == 170)
                {
                    int cPara1 = (int)str[1];
                    int cPara2 = (int)str[2];
                    int cPara3 = (int)str[3];
                    napi_create_object(envs, &jsObj); //创建JS回调函数对应的参数对象
                    napi_create_int32(envs, cPara1, &prop1);
                    napi_create_int32(envs, cPara2, &prop2);
                    napi_create_int32(envs, cPara3, &prop3);
                    napi_set_named_property(envs, jsObj, "prop1", prop1); //设置JS参数对象属性值
                    napi_set_named_property(envs, jsObj, "prop2", prop2);
                    napi_set_named_property(envs, jsObj, "prop3", prop3);
                    napi_call_function(envs, nullptr, callback, 1, &jsObj, &undefine); //使用生成的JS参数，调用对应的JS回调函数
                }
                buflen = strlen(str);
                //send(client_st, str, strlen(str), 0);
            }
        
    }

    return NULL;
}


//打开TCP服务器
static napi_value ServerStart(napi_env env, napi_callback_info info)
{
    
    size_t argc = 1; //参数个数定义
    napi_value argv[argc];
    napi_value thisVar = nullptr;
    void *data = nullptr;
    envs = env;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
    NAPI_ASSERT(env, argc >= 1, "JSCallback Wrong number of arguments"); //参数个数校验

    napi_valuetype callbackType = napi_undefined;
    napi_typeof(env, argv[0], &callbackType);
    NAPI_ASSERT(env, callbackType == napi_function, "parameter 1 type mismatch"); //参数类型校验，传进来的须是函数类型

	napi_create_reference(env, argv[0], 1, &CallbackReff);  //创建引用
	//napi_get_reference_value(env, CallbackRef, &callback); //根据引用获取回调函数callback

    pthread_t thrd;
    HILOG_INFO("test0002 thrs start!");
    //pthread_create(&thrd1, NULL, recvsocket, &client_st);
    pthread_create(&thrd, NULL, socketserverthrd,NULL);

    HILOG_INFO("test0002  end!");
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}


static napi_value ServerRead(napi_env env, napi_callback_info info)
{
    napi_value result = nullptr;

    napi_create_string_utf8(env, buf,buflen, &result);

    return result;
}

static napi_value ServerStop(napi_env env, napi_callback_info info)
{
    close(st);
    wflag = 0;
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

//socker 发送
static napi_value ServerWrite(napi_env env, napi_callback_info info)
{
    size_t requireArgc = 3;
    size_t argc = 3;
    napi_value args[3] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
    napi_valuetype valuetype1;
    NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
    napi_valuetype valuetype2;
    NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));

    NAPI_ASSERT(env, valuetype0 == napi_number && valuetype1 == napi_number && valuetype2 == napi_number, "Wrong argument type. Numbers expected.");
    
    char str[4];
    uint32_t a,b,c;
    NAPI_CALL(env, napi_get_value_uint32(env, args[0], &a));
    NAPI_CALL(env, napi_get_value_uint32(env, args[1], &b));
    NAPI_CALL(env, napi_get_value_uint32(env, args[2], &c));
    str[0] = (char)0xAA;
    str[1] = (char)a;
    str[2] = (char)b;
    str[3] = (char)c;

    if (-1 == write(client_st, str,4)){
        HILOG_INFO("test0002 okok servertest  error");
    }
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}




EXTERN_C_START
/*
 * function for module exports
 */
static napi_value Init(napi_env env, napi_value exports)
{
    /*
     * Properties define
     */
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("switchLed", SwitchLed),
        DECLARE_NAPI_FUNCTION("JSCallback", JSCallback),
        DECLARE_NAPI_FUNCTION("ServerStart", ServerStart),
        DECLARE_NAPI_FUNCTION("ServerRead", ServerRead),
        DECLARE_NAPI_FUNCTION("ServerStop", ServerStop),
        DECLARE_NAPI_FUNCTION("ServerWrite", ServerWrite)

    };
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));

    //LedJavascriptClassInit(env, exports);

    return exports;
}

EXTERN_C_END

/*
 * Module define
 */
static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "netanddev",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};
/*
 * Module register function
 */
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
    napi_module_register(&demoModule);
}
